<template>
  <div class="h-full relative">
    <div class="h-full w-full absolute top-0 left-0">
      <div ref="containerRef" class="h-full p-2 box-border"></div>
      <teleport-container></teleport-container>
    </div>
    <div ref="contextMenuRef" v-if="show" class="context-menu absolute p-2">
      <ul class="h-full text-center w-32">
        <li @click="handleAdd('pool-item')">添加池</li>
        <li @click="handleAdd('pump-item')">添加泵</li>
        <li @click="handleAdd('empty-item')">添加空节点</li>
        <li @click="handleAdd('other-item')">添加探头</li>
        <li @click="handleAdd('pipeline-item')">添加管道</li>
        <li @click="handleAdd('valve-item')">添加阀</li>
        <li @click="handleAdd('flow-meter')">添加流量计</li>
        <li @click="handleAdd('text-item')">添加文字</li>
        <li @click="handleSave">保存到本地</li>
        <li @click="handleExport">导出数据</li>
        <li @click="handleImport">导入数据</li>
        <li @click="handleClear">清空画布</li>
        <li @click="handleBackground">设置背景</li>
      </ul>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, provide, ref } from 'vue'
import { downloadFile, initGraph } from '@/utils/x6'
import { getTeleport } from '@antv/x6-vue-shape'
import { registerEvent } from '@/utils/x6/event'
import { storeToRefs } from 'pinia'
import { useContextMenu } from '@/stores/useContextMenu'
import { onClickOutside, useFileDialog } from '@vueuse/core'
import { type Graph } from '@antv/x6'
import { Selection } from '@antv/x6-plugin-selection'
import { Transform } from '@antv/x6-plugin-transform'
import { Snapline } from '@antv/x6-plugin-snapline'
import { Keyboard } from '@antv/x6-plugin-keyboard'

import { useDeviceData } from '@/stores/useWebSocket'
import { useRoute } from 'vue-router'
import { useMessage, useMessageBox } from '@/hooks/message'
import KeywordsEvent from '@/utils/x6/keywords'

const route = useRoute()
const wsStore = useDeviceData()
wsStore.linkWs(route.query.userId as string)

const TeleportContainer = getTeleport()

const containerRef = ref<HTMLDivElement>()
const contextMenuRef = ref<HTMLDivElement>()

const store = useContextMenu()

const { show, x, y } = storeToRefs(store)
const graph = ref<Graph>()
provide('graph', graph)
const rect = ref<DOMRect>()
onMounted(() => {
  rect.value = containerRef.value?.getBoundingClientRect()
  graph.value = initGraph(containerRef.value!, rect.value!)
  initStartAndEnd(graph.value, rect.value)

  graph.value.addNode({
    shape: 'pool-item',
    x: 400,
    y: 400,
    data: {
      color: '#387af6'
    },
    ports: {
      items: [
        {
          group: 'left'
        },
        {
          group: 'right'
        }
      ]
    }
  })
  registerEvent(graph.value)
  // graph.value.use(
  //   new Transform({
  //     resizing: {
  //       enabled(node: Node) {
  //         return node.shape !== 'empty-item'
  //       }
  //     }
  //   })
  // )
  graph.value.use(
    new Selection({
      enabled: true
    })
  )
  // graph.value.use(
  //   new Scroller({
  //     enabled: true,
  //   })
  // )

  graph.value.use(
    new Transform({
      resizing: {
        enabled: true
      },
      rotating: {
        enabled(node) {
          return node.shape === 'pump-item' || node.shape === 'pipeline-item'
        },
        grid: 90
      }
    })
  )
  graph.value.use(
    new Snapline({
      enabled: true
    })
  )

  graph.value.use(
    new Keyboard({
      enabled: true,
      global: true
    })
  )
  new KeywordsEvent(graph.value)
})

const initStartAndEnd = function (graph?: Graph, rect?: DOMRect) {
  graph?.addNode({
    id: 'begin',
    shape: 'begin',
    x: 0,
    y: (rect?.height || 0) / 2 - 15,
    ports: {
      items: [
        {
          id: 'start',
          group: 'right'
        }
      ]
    }
  })
  graph?.addNode({
    id: 'end',
    shape: 'end',
    x: (rect?.width || 0) - 100,
    y: (rect?.height || 0) / 2 - 20,
    ports: {
      items: [
        {
          id: 'end',
          group: 'left'
        }
      ]
    }
  })
}

onClickOutside(contextMenuRef, () => {
  store.hideMenu()
})

const handleAdd = async function (shape: string) {
  const _x = x.value
  const _y = y.value
  const node = graph.value?.addNode({
    shape: shape,
    x: _x,
    y: _y,
    ports: {
      items: [
        {
          group: 'left'
        },
        {
          group: 'right'
        }
      ]
    }
  })
  node?.setZIndex(Math.round(Math.random() * 100))
  store.hideMenu()
}

const handleExport = function () {
  const json = graph.value?.toJSON()
  downloadFile(JSON.stringify(json, null, '\t'), `导出数据.json`)
  store.hideMenu()
}

const { open, onChange } = useFileDialog({
  accept: 'application/json', // Set to accept only image files
  directory: false // Select directories instead of files if set true
})

onChange((files) => {
  const file = files?.item(0)
  const fileReader = new FileReader()
  fileReader.onload = function () {
    const json = JSON.parse(fileReader.result as string)
    graph.value?.fromJSON(json)
    graph.value?.center()
  }
  fileReader.readAsText(file as File)
})
const handleImport = async function () {
  store.hideMenu()
  open()
}

const handleClear = function () {
  graph.value?.clearCells()
  initStartAndEnd(graph.value, rect.value)
  store.hideMenu()
}

const top = computed(() => y.value + 'px')
const left = computed(() => x.value + 'px')

const handleSave = function () {
  const json = graph.value?.toJSON()
  window.localStorage.setItem('graph', JSON.stringify(json))
  useMessage().success('保存成功')
  store.hideMenu()
}

const handleOpen = function () {
  const text = window.localStorage.getItem('graph')
  if (!text) {
    useMessage().error('未发现本地数据')
    store.hideMenu()
    return
  }
  graph.value?.fromJSON(JSON.parse(text))
  store.hideMenu()
}
onMounted(handleOpen)

const handleBackground = function () {
  useMessageBox()
    .prompt('请输入背景图URL')
    .then(({ value }) => {
      console.log(value)
      if (value) {
        graph.value?.drawBackground({
          image: value,
          size: '100% 100%'
        })
      } else {
        graph.value?.clearBackground()
      }
    })
}
</script>

<style scoped>
.context-menu {
  top: v-bind('top');
  left: v-bind('left');
  box-shadow: 0 0 15px 0 rgba(46, 112, 236, 0.3);
  border-radius: 10px;
  background-color: white;
}
</style>
